package fr.kirin.logger.model;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;

import fr.kirin.logger.GraphicLogger;
import fr.kirin.logger.filter.TreeFilter;
import fr.kirin.logger.model.gui.TreeDisplayer;


/**
 * 
 * Default tree model for logger events. 
 * 
 * @author kirin
 *
 */
public class DefaultLoggerTreeModel extends DefaultTreeModel implements LoggerTreeModel {

    /**
     * Bean which contains informations about nodes.
     * 
     * @author kirin
     *
     */
    public static class InfoBean {
        private final String value;
        private final Level level;
        private String comment;

        /**
         * 
         * @param value node value
         * @param level node level
         */
        public InfoBean(String value, Level level)
        {
            this(value,level,"");
        }
        /**
         * 
         * @return the level
         */
        public Level getLevel()
        {
            return level;
        }
        /**
         * 
         * @return
         */
        public String getComment()
        {
            return comment;
        }
        /**
         * 
         * 
         * @param value node value.
         * @param level node level.
         * @param comment node comment.
         */
        public InfoBean(String value, Level level, String comment)
        {
            this.value = value;
            this.level = level;
            this.comment = comment;
        }
        /**
         * @return the value
         */
        public String getValue() {
            return value;
        }


        @Override
        public String toString() {
            return getValue();
        }
        public void setComment(String comment)
        {
            this.comment = comment;
        }
    }


    private DefaultLoggerTreeModel(MutableTreeNode root) {
        super(root);

    }

    /**
     * 
     *Construct a tree model with the name of the root. 
     * @param root root's name.
     */
    public DefaultLoggerTreeModel(String root){
        this(new DefaultMutableTreeNode(new InfoBean(root,DefaultLevel.NONE)));
    }


    @SuppressWarnings("unchecked")
    private DefaultMutableTreeNode getNodeForInsert(String[] categories, DefaultMutableTreeNode root,Level level){
        DefaultMutableTreeNode top = root;
        boolean childFound = false;

        if(categories.length == 1)
            return top;

        for(String s : categories){
            if(s.length() <= 0)
                continue;
            Enumeration<DefaultMutableTreeNode> children = top.children();
            while(children.hasMoreElements()){
                DefaultMutableTreeNode curNode = (DefaultMutableTreeNode) children.nextElement();
                InfoBean bean = (InfoBean) curNode.getUserObject();
                String cat = bean.getValue();
                if(s.equals(cat)){
                    top= curNode;
                    childFound = true;
                    break;
                }
            }

            if(!childFound){
                DefaultMutableTreeNode addNode = new DefaultMutableTreeNode(new InfoBean(s,level));
                top.add(addNode);
                top = addNode;

            }
            childFound = false;

        }

        return top;
    }

    /**Accept a node or not.
     * 
     * @param node node to check.
     * @return true if the node is accepted , false else.
     */
    private boolean accept(TreeNode node){
        for(TreeFilter f : filtersList)
            if(!f.accept(node))
                return false;

        return true;
    }

    /**
     */
    public Object getChild(Object parent, int index) {
        int realchildren = ((TreeNode) parent).getChildCount();
        if (filtersList.isEmpty()) {
            return ((TreeNode) parent).getChildAt(index);
        }
        if (realchildren == 0) {
            throw new ArrayIndexOutOfBoundsException("node has no children");
        }

        int realIndex    = -1;
        int visibleIndex = -1;
        for (int i = 0; i < realchildren; i++) {
            TreeNode node = (TreeNode) ((TreeNode) parent).getChildAt(i);
            if (accept(node)) {
                visibleIndex++;
            }
            realIndex++;
            if (visibleIndex == index) {
                return ((TreeNode) parent).getChildAt(realIndex);
            }
        }

        throw new ArrayIndexOutOfBoundsException("index unmatched");
    }

    /**
     */
    public int getChildCount(Object parent) {
        int realchildren = ((TreeNode) parent).getChildCount();
        if (filtersList.isEmpty()) {
            return realchildren;
        }
        if (realchildren == 0) {
            return 0;
        }

        int count = 0;
        for (int i = 0; i < realchildren; i++) {
            TreeNode node = (TreeNode) ((TreeNode) parent).getChildAt(i);
            if (accept(node)) {
                count++;
            }
        }

        return count;
    }


    public void addItem(String category, String value,Level level){
        addItem(category, value, "", level);
    }

    public void addItem(String category, String value, String comment,
            Level level)
    {
        DefaultMutableTreeNode top = getNodeForInsert(category.split("/"), (DefaultMutableTreeNode) root,level);
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new InfoBean(level.getShortName()+" "+value,level,comment));
        top.add(newNode);
        reload();

    }


    public static void main(String[] args) {
        JFrame frame = new JFrame();
        //DefaultMutableTreeNode node = new DefaultMutableTreeNode("Category");

//      final DefaultLoggerTreeModel loggerTreeModel = new DefaultLoggerTreeModel("Category");

        final GraphicLogger log = GraphicLogger.getInstance();
        log.getGui().show();
        TreeDisplayer td = new TreeDisplayer(log.getModel());
//      td.setCellRenderer(new LoggerCellRenderer());

        frame.add(td.getUI());

        final Random r = new Random();
        JButton b = new JButton("add");
        b.addActionListener(new ActionListener() {

            int i=0;
            public void actionPerformed(ActionEvent e) {
                GraphicLogger.getInstance().log("/plop"+r.nextInt()+"//plap/plip"+i++, "test"+r.nextInt(500), DefaultLevel.values()[r.nextInt(20)%DefaultLevel.values().length],"comment "+r.nextInt());

            }

        });
        frame.add(b,BorderLayout.SOUTH);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300,300);
        frame.setVisible(true);
    }

    public void sort() {
        sort((DefaultMutableTreeNode) getRoot());
    }

    @SuppressWarnings("unchecked")
    public void sort(DefaultMutableTreeNode node){
        Enumeration<DefaultMutableTreeNode> children = node.children();
        ArrayList<DefaultMutableTreeNode> list = Collections.list(children);
        Collections.sort(list,new Comparator<DefaultMutableTreeNode>() {
            public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
                InfoBean b1 = (InfoBean) o1.getUserObject();
                InfoBean b2 = (InfoBean) o2.getUserObject();
                return b1.getValue().compareTo(b2.getValue());
            }
        });

        node.removeAllChildren();
        for(DefaultMutableTreeNode n : list)
            node.add(n);


        nodeStructureChanged(node);
        //reload();
    }


    public void addFilter(TreeFilter filter)
    {
        filtersList.add(filter);
        reload();

    }

    private final ArrayList<TreeFilter> filtersList = new ArrayList<TreeFilter>();

    public List<TreeFilter> getFilters()
    {
        return filtersList;
    }

    public boolean removeFilter(TreeFilter filter)
    {

        boolean b =  filtersList.remove(filter);
        if(b)
            reload();
        return b;

    }



}
